package com.lxf.cloudorder.service.impl;

import cn.afterturn.easypoi.word.WordExportUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.lxf.cloudfeign.client.configsClient.ConfigClient;
import com.lxf.cloudfeign.client.productClient.ProductClient;
import com.lxf.cloudfeign.client.userClient.UserClient;
import com.lxf.cloudorder.bean.OrderBean;
import com.lxf.cloudorder.mapper.OrderItemRepository;
import com.lxf.cloudorder.mapper.OrderMapper;
import com.lxf.cloudorder.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Response;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 罗兴发
 * @data 2025/5/8 17:40
 * @description：订单业务员实现层
 */
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Resource
    OrderMapper orderMapper;
    @Resource
    private ProductClient productClient;
    @Resource
    private UserClient userClient;
    @Resource
    private OrderItemRepository orderRepository;
    @Resource
    private ConfigClient configClient;
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    //redisson分布式锁
    private final RedissonClient redissonClient;

    private final static String LOCK = "I_AM_ORDER_LOCK";

    public OrderServiceImpl(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)//开启事务
    public String addOrder(JSONObject json, HttpServletRequest request) {
        JSONObject js = new JSONObject();
        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        JSONObject j1 = new JSONObject();
        j1.put("redis_key",secretToken);
        //获取redis中缓存的token
        String o = userClient.getRedisData(j1);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);
            Integer userId = Integer.valueOf(userIdSub);//用户ID
            RLock rLock = redissonClient.getLock(LOCK);
            try {
//                rLock.tryLock(0,12,TimeUnit.SECONDS);
//                //获取锁,持有锁时间10秒，十秒后未处理完，则自动释放锁，避免死锁
//                rLock.lock(10, TimeUnit.SECONDS);
                rLock.lock();
                //获取下单商品的对象(购物车功能)
                JSONArray orderList = json.getJSONArray("order_list");
                log.info("订单列表：",orderList);
                //商品数组
                List<OrderBean> list = new ArrayList<>();
                //处理订单数据
                for (int i=0;i<orderList.size();i++) {
                    JSONObject jsonObject = orderList.getJSONObject(i);
                    log.info("实体类：{}",jsonObject);

                    //商品编码
                    String goodCode = jsonObject.getString("good_code");

                    //查询商品详情信息接口
                    JSONObject j = new JSONObject();
                    j.put("good_code",goodCode);
                    String product = productClient.getProductByCode(j);

                    //数据转换
                    JSONObject object = JSONObject.parseObject(product);

                    //根据商品编码查询到的商品详情信息
                    JSONObject data = object.getJSONObject("data");
                    String goodName = data.getString("goodName");
                    log.info("商品详情信息：",data);

                    JSONObject j4 = new JSONObject();
                    j4.put("redis_key",goodCode);

                    //从redis中根据商品编码查询商品库存信息（必须保证编码的唯一性）
                    String s = userClient.getRedisData(j4);

                    //商品剩余数量
                    Integer num = Integer.valueOf(s);

                    //下单的商品数量
                    Integer nums = jsonObject.getInteger("good_num");

                    //扣除下单商品后，商品最终库存量
                    int pNum = num - nums;
                    if (nums<=num){

                        //商品单价
                        BigDecimal unitPrice = data.getBigDecimal("goodPrice");

                        //时间规格化
                        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String addTime = sf.format(new Date());

                        //获取时间戳
                        long l = System.currentTimeMillis();

                        //订单实例化
                        OrderBean order = new OrderBean();
                        order.setOrderCode("kg"+l);
                        order.setPNum(nums);
                        order.setUserId(userId);
                        order.setPCode(goodCode);
                        order.setUnitPrice(unitPrice);
                        order.setSurplusNum(pNum);

                        //总价
                        BigDecimal totalPrice = unitPrice.multiply(BigDecimal.valueOf(nums));
                        order.setTotalPrice(totalPrice);
                        order.setAddTime(addTime);
                        list.add(order);

                        //加入es搜索引擎中
                        orderRepository.save(order);
                    }else {
                        js.put("code",401);
                        js.put("msg","The Goods Number Is Lack");

                        String msg = "您的下单【商品名称-"+goodName+"、编码："+goodCode+"、数量："+nums+"件】因库存不足，导致无法下单，请稍后重试！";
                        JSONObject j2 = new JSONObject();
                        j2.put("user_id",userIdSub);
                        j2.put("msg",msg);
                        j2.put("msg_type",2);
                        j2.put("f_user_id",2);

                        //发送消息
                        userClient.sendPrivateMsg(j2);
                    }
                }
                if (list.size()>0){
                    //创建订单
                    int flag = orderMapper.addOrder(list);
                    if (flag<0){
                        js.put("code",401);
                        js.put("msg","The Order Insert Fails");
                    }else {
                        js.put("code",200);
                        js.put("msg","Success");

                        JSONObject j5 = new JSONObject();
                        for (OrderBean ob:list) {
                            String goodCode = ob.getPCode();
                            Integer goodNum = ob.getSurplusNum();

                            j5.put("keys",goodCode);
                            j5.put("data",goodNum);

                            //更新库存信息
                            userClient.addRedisData(j5);
                            JSONObject j = new JSONObject();
                            j.put("pCode",goodCode);
                            j.put("pNum",goodNum);
                            productClient.updateProductNumByCode(j);
                        }
                        String msg = "您成功已下单，待支付中.......";
                        JSONObject j2 = new JSONObject();
                        j2.put("user_id",userIdSub);
                        j2.put("msg",msg);
                        j2.put("msg_type",2);
                        j2.put("f_user_id",2);

                        //发送消息
                        userClient.sendPrivateMsg(j2);
                    }
                }
            }catch (Exception e){
                String message = e.getMessage();
                js.put("code",500);
                js.put("msg",message);
            }finally {
                //释放锁
                rLock.unlock();
            }
        }else {
            js.put("code",400);
            js.put("msg","The Current Request is Invalid");
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String getOrderByUserId(JSONObject json) {
        JSONObject js = new JSONObject();
        Integer userId = json.getInteger("user_id");
        Integer page = json.getInteger("page");
        Integer size = json.getInteger("size");

        // 计算初始页
        Integer startPage = (page - 1) * size;

        // 查询总条数
        Integer count = orderMapper.getOrderCountByUserId(userId);
        List<OrderBean> list = orderMapper.getOrderByUserId(userId,startPage,size);
        js.put("code",200);
        js.put("msg","success");
        js.put("data",list);
        return JSONObject.toJSONString(js,SerializerFeature.WriteMapNullValue);
    }

    /**
     * 订单生成word文档
     * @param json
     * @param request
     * @return
     */
    @Override
    public String printOrderById(JSONObject json,HttpServletRequest request) {
        JSONObject js = new JSONObject();
        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        JSONObject j1 = new JSONObject();
        j1.put("redis_key",secretToken);
        //获取redis中缓存的token
        String o = userClient.getRedisData(j1);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);

            String orderCode = json.getString("order_code");

            // 根据ID获取订单详情
            Optional<OrderBean> orders = orderRepository.findById(orderCode);
            if (orders.isPresent()){
                OrderBean order = orders.get();

                // 获取商品编号
                String pCode = order.getPCode();

                JSONObject j  = new JSONObject();
                j.put("good_code",pCode);
                // 根据商品编号获取商品详情信息
                String p = productClient.getProductByCode(j);
                JSONObject jsonObject = JSONObject.parseObject(p);

                // 获取商品对象信息
                JSONObject data = jsonObject.getJSONObject("data");
                String goodName = data.getString("goodName");

                // 获取文件存放地址
                String uploadPath = configClient.getUploadPath();

                j.put("user_id",userIdSub);
                // 获取下单用户信息
                String u = userClient.getUserByUserId(j);
                JSONObject jsonObject1 = JSONObject.parseObject(u);
                // 获取商品对象信息
                JSONObject data1 = jsonObject1.getJSONObject("data");
                String userName = data1.getString("userName");

                // 文件名称
                String wordPath = "\\modelWord.docx";

                // 文件地址
                String filePath = uploadPath+wordPath;
                try {
                    // 随机生成新文件名称
                    UUID uuid = UUID.randomUUID();
                    String s = uuid.toString();
                    String files = s+".docx";

                    // 数据封装
                    Map<String,Object> map = new HashMap<>();
                    map.put("userName",userName);//下单用户名称
                    map.put("orderCode",orderCode);//订单流水号
                    map.put("pCode",pCode);//商品编码
                    map.put("pNum",order.getPNum());//商品数量
                    map.put("goodName",goodName);//商品名称
                    map.put("unitPrice",order.getUnitPrice());//商品单价
                    map.put("totalPrice",order.getTotalPrice());//订单总价
                    map.put("addTime",order.getAddTime());//下单时间

                    // 调用方法生成word
                    exportWord(filePath,uploadPath,files,map,request);

                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }else {
            js.put("code",400);
            js.put("msg","The Current Request is Invalid");
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String getOrderByGoodName(JSONObject json, HttpServletRequest request) {
        JSONObject js = new JSONObject();
        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        JSONObject j1 = new JSONObject();
        j1.put("redis_key",secretToken);
        //获取redis中缓存的token
        String o = userClient.getRedisData(j1);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);

            // 前端参数
            String goodName = json.getString("good_name");
            Integer page = json.getInteger("page");
            Integer size = json.getInteger("size");

            // 计算初始页
            Integer startPage = (page-1)*size;

            // 1. 构建查询条件
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.multiMatchQuery(goodName, "goodName"))
                    .withQuery(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("userId",userIdSub)))
                    .withPageable(PageRequest.of(startPage, size));

            // 2. 添加高亮配置
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("goodName")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");
            queryBuilder.withHighlightBuilder(highlightBuilder);

            // 3. 执行查询
            SearchHits<OrderBean> hits = elasticsearchRestTemplate
                    .search(queryBuilder.build(), OrderBean.class);

            // 4. 处理高亮结果
            List<OrderBean> list = hits.stream()
                    .map(hit -> {
                        OrderBean doc = hit.getContent();
                        Map<String, List<String>> highlightFields = hit.getHighlightFields();
                        highlightFields.forEach((field, texts) -> {
                            try {
                                Field declaredField = doc.getClass().getDeclaredField(field);
                                declaredField.setAccessible(true);
                                declaredField.set(doc, texts.get(0));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        });
                        return doc;
                    }).collect(Collectors.toList());

            // 7. 构造分页结果
            PageImpl<OrderBean> orderBeans = new PageImpl<>(list, PageRequest.of(startPage, size), hits.getTotalHits());

            js.put("data",orderBeans);
            js.put("code",200);
            js.put("msg","Success");
        }else {
            js.put("code",400);
            js.put("msg","The Current Request is Invalid");
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    /**
     * 导出word
     * <p>第一步生成替换后的word文件，只支持docx</p>
     * <p>第二步下载生成的文件</p>
     * <p>第三步删除生成的临时文件</p>
     * 模版变量中变量格式：{{foo}}
     * @param templatePath word模板地址
     * @param temDir 生成临时文件存放地址
     * @param fileName 文件名
     * @param params 替换的参数
     * @param request HttpServletRequest
     */
    public static void exportWord(String templatePath, String temDir, String fileName, Map<String, Object> params, HttpServletRequest request) {
        Assert.notNull(templatePath,"模板路径不能为空");
        Assert.notNull(temDir,"临时文件路径不能为空");
        Assert.notNull(fileName,"导出文件名不能为空");
        Assert.isTrue(fileName.endsWith(".docx"),"word导出请使用docx格式");
        if (!temDir.endsWith("/")){
            temDir = temDir + File.separator;
        }
        File dir = new File(temDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        try {
            String userAgent = request.getHeader("user-agent").toLowerCase();
            if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
                fileName = URLEncoder.encode(fileName, "UTF-8");
            } else {
                fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
            }
            XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
            String tmpPath = temDir + fileName;
            FileOutputStream fos = new FileOutputStream(tmpPath);
            doc.write(fos);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param path       下载后的文件路径和名称
     * @param netAddress 文件所在网络地址
     * @功能描述 网络文件下载到服务器本地
     */
    public void downloadNet(String netAddress, String path){
        try {
            URL url = new URL(netAddress);
            URLConnection conn = url.openConnection();
            InputStream inputStream = conn.getInputStream();

            // 创建下载文件夹
            if (!path.endsWith("/")){
                path = path + File.separator;
            }
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            // 文件输出
            FileOutputStream fileOutputStream = new FileOutputStream(path);

            int bytesum = 0;
            int byteread;
            byte[] buffer = new byte[1024];
            while ((byteread = inputStream.read(buffer)) != -1) {
                bytesum += byteread;
                System.out.println(bytesum);
                fileOutputStream.write(buffer, 0, byteread);
            }
            fileOutputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
